home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / bipl.zip / PROCS.ZIP / GETCHLIB.ICN < prev    next >
Text File  |  1993-01-27  |  9KB  |  332 lines

  1. ############################################################################
  2. #
  3. #    File:     getchlib.icn
  4. #
  5. #    Subject:  Procedures for getch for UNIX
  6. #
  7. #    Author:   Richard L. Goerwitz
  8. #
  9. #    Date:     January 17, 1992
  10. #
  11. ###########################################################################
  12. #
  13. #    Version:  1.14
  14. #
  15. ###########################################################################
  16. #
  17. #  Implementing getch() is a much, much more complex affair under UNIX
  18. #  than it is under, say, MS-DOS.  This library represents one,
  19. #  solution to the problem - one which can be run as a library, and
  20. #  need not be compiled into the run-time system.  Note that it will
  21. #  not work on all systems.  In particular, certain Suns (with a
  22. #  screwy stty command) and the NeXT 1.0 OS (lacking the -g option for
  23. #  stty) do not run getchlib properly.  See the bugs section below for
  24. #  workarounds.
  25. #
  26. #  Four basic utilities are included here:
  27. #
  28. #    getch()        - waits until a keystroke is available &
  29. #        returns it without displaying it on the screen
  30. #    getche()    - same as getch() only with echo
  31. #    getse(s)    - like getche() only for strings.  The optional
  32. #        argument s gives getse() something to start with.  Use this
  33. #           if, say, you want to read single characters in cbreak mode,
  34. #           but get more input if the character read is the first part
  35. #           of a longer command.  If the user backspaces over everything
  36. #           that has been input, getse() fails.  Returns on \r or \n.
  37. #    reset_tty()    - absolutely vital routine for putting the cur-
  38. #           rent tty line back into cooked mode; call it before exiting
  39. #           or you will find yourself with a locked-up terminal; use it
  40. #           also if you must temporarily restore the terminal to cooked
  41. #           mode
  42. #
  43. #  Note that getse() *must* be used in place of read(&input) if you
  44. #  are planning on using getch() or getche(), since read(&input)
  45. #  assumes a tty with "sane" settings.
  46. #
  47. #  Warning:  The routines below do not do any sophisticated output
  48. #  processing.  As noted above, they also put your tty line in raw
  49. #  mode.  I know, I know:  "Raw is overkill - use cbreak."  But in
  50. #  a world that includes SysV, one must pick a lowest common denomi-
  51. #  nator.  And no, icanon != cbreak.
  52. #
  53. #  BUGS: These routines will not work on systems that do not imple-
  54. #  ment the -g option for the stty command.  The NeXT workstation is
  55. #  an example of such a system.  Tisk, tisk.  If you are on a BSD
  56. #  system where the network configuration makes stty | more impossible,
  57. #  then substitute /usr/5bin/stty (or whatever your system calls the
  58. #  System V stty command) for /bin/stty in this file.  If you have no
  59. #  SysV stty command online, then you can try replacing every instance
  60. #  of "stty -g 2>&1" below with "stty -g 2>&1 1> /dev/tty" or
  61. #  something similar.
  62. #
  63. ############################################################################
  64. #
  65. #  Example program:
  66. #
  67. #      The following program is a simple file viewer.  To run, it
  68. #  needs to be linked with itlib.icn, iscreen.icn, and this file
  69. #  (getchlib.icn).
  70. #
  71. #  procedure main(a)
  72. #
  73. #      # Simple pager/file searcher for UNIX systems.  Must be linked
  74. #      # with itlib.icn and iscreen.icn.
  75. #  
  76. #      local intext, c, s
  77. #  
  78. #      # Open input file
  79. #      intext := open(a[1],"r") | {
  80. #      write(&errout,"Can't open input file.")
  81. #      exit(1)
  82. #      }
  83. #  
  84. #      # Initialize screen
  85. #      clear()
  86. #      print_screen(intext) | exit(0)
  87. #  
  88. #      # Prompt & read input
  89. #      repeat {
  90. #      iputs(igoto(getval("cm"), 1, getval("li")))
  91. #      emphasize()
  92. #      writes("More? (y/n or /search):")
  93. #      write_ce(" ")
  94. #      case c := getche() of {
  95. #          "y" : print_screen(intext) | break
  96. #          " " : print_screen(intext) | break
  97. #          "n" : break
  98. #          "q" : break
  99. #          "/" : {
  100. #          iputs(igoto(getval("cm"), 1, getval("li")))
  101. #          emphasize()
  102. #          writes("Enter search string:")
  103. #          write_ce(" ")
  104. #          pattern := GetMoreInput()
  105. #          /pattern | "" == pattern & next
  106. #          # For more complex patterns, use findre() (IPL findre.icn)
  107. #          if not find(pattern, s := !intext) then {
  108. #              iputs(igoto(getval("cm"), 1, getval("li")))
  109. #              emphasize()
  110. #              write_ce("String not found.")
  111. #              break
  112. #          }
  113. #          else print_screen(intext, s) | break
  114. #          }
  115. #      }
  116. #      }
  117. #  
  118. #      reset_tty()
  119. #      write()
  120. #      exit(0)
  121. #
  122. #  end
  123. #  
  124. #  procedure GetMoreInput(c)
  125. #  
  126. #      local input_string
  127. #      static BS
  128. #      initial BS := getval("bc") | "\b"
  129. #  
  130. #      /c := ""
  131. #      if any('\n\r', chr := getch())
  132. #      then return c
  133. #      else {
  134. #      chr == BS & fail
  135. #      writes(chr)
  136. #      input_string := getse(c || chr) | fail
  137. #      if any('\n\r', input_string)
  138. #      then fail else (return input_string)
  139. #      }
  140. #  
  141. #  end
  142. #  
  143. #  procedure print_screen(f,s)
  144. #  
  145. #      if /s then
  146. #      begin := 1
  147. #      # Print top line, if one is supplied
  148. #      else {
  149. #      iputs(igoto(getval("cm"), 1, 1))
  150. #      write_ce(s ? tab(getval("co") | 0))
  151. #      begin := 2
  152. #      }
  153. #  
  154. #      # Fill the screen with lines from f; clear and fail on EOF.
  155. #      every i := begin to getval("li") - 1 do {
  156. #      iputs(igoto(getval("cm"), 1, i))
  157. #      if not write_ce(read(f) ? tab(getval("co") | 0)) then {
  158. #          # Clear remaining lines on the screen.
  159. #          every j := i to getval("li") do {
  160. #          iputs(igoto(getval("cm"), 1, j))
  161. #          iputs(getval("ce"))
  162. #          }
  163. #          iputs(igoto(getval("cm"), 1, i))
  164. #          fail
  165. #      }
  166. #      }
  167. #      return
  168. #  
  169. #  end
  170. #  
  171. #  procedure write_ce(s)
  172. #  
  173. #      normal()
  174. #      iputs(getval("ce")) |
  175. #      writes(repl(" ",getval("co") - *s))
  176. #      writes(s)
  177. #      return
  178. #
  179. #  end
  180. #
  181. ############################################################################
  182. #
  183. #  Requires: UNIX
  184. #
  185. #  Links: itlib.icn
  186. #
  187. ############################################################################
  188.  
  189.  
  190. global c_cc, current_mode        # what mode are we in, raw or cooked?
  191. record termio_struct(vintr,vquit,verase,vkill)
  192.  
  193. procedure getse(s)
  194.  
  195.     # getse() - like getche, only for strings instead of single chars
  196.     #
  197.     # This procedure *must* be used instead of read(&input) if getch
  198.     # and/or getche are to be used, since these put the current tty
  199.     # line in raw mode.
  200.     #
  201.     # Note that the buffer can be initialized by calling getse with a
  202.     # string argument.  Note also that, as getse now stands, it will
  203.     # fail if the user backspaces over everything that has been input.
  204.     # This change does not coincide with its behavior in previous ver-
  205.     # sions.  It can be changed by commenting out the line "if *s < 1
  206.     # then fail" below, and uncommenting the line "if *s < 1 then
  207.     # next."
  208.  
  209.     local chr
  210.     static BS
  211.     initial {
  212.     BS := getval("bc") | "\b"
  213.     if not getval("bs") then {
  214.         reset_tty()
  215.         stop("Your terminal can't backspace!")
  216.     }
  217.     }
  218.  
  219.     /s := ""
  220.     repeat {
  221.     case chr := getch() | fail of {
  222.         "\r"|"\n"    : return s
  223.         c_cc.vkill   : {
  224.         if *s < 1 then next
  225.         every 1 to *s do writes(BS)
  226.         s := ""
  227.         }
  228.         c_cc.verase   : {
  229.         # if *s < 1 then next
  230.         writes(BS) & s := s[1:-1]
  231.         if *s < 1 then fail
  232.         }
  233.         default: writes(chr) & s ||:= chr
  234.     }
  235.     }
  236.  
  237. end
  238.  
  239.  
  240.  
  241. procedure setup_tty()
  242.     change_tty_mode("setup")
  243.     return
  244. end
  245.  
  246.  
  247.  
  248. procedure reset_tty()
  249.  
  250.     # Reset (global) mode switch to &null to show we're in cooked mode.
  251.     current_mode := &null
  252.     change_tty_mode("reset")
  253.     return
  254.  
  255. end
  256.  
  257.  
  258.  
  259. procedure getch()
  260.  
  261.     local chr
  262.  
  263.     # If the global variable current_mode is null, then we have to
  264.     # reset the terminal to raw mode.
  265.     if /current_mode := 1 then
  266.     setup_tty()
  267.  
  268.     chr := reads(&input)
  269.     case chr of {
  270.     c_cc.vintr : reset_tty() & stop()  # shouldn't hard code this in
  271.     c_cc.vquit  : reset_tty() & stop()
  272.     default : return chr
  273.     }
  274.  
  275. end
  276.  
  277.  
  278.  
  279. procedure getche()
  280.  
  281.     local chr
  282.  
  283.     # If the global variable current_mode is null, then we have to
  284.     # reset the terminal to raw mode.
  285.     if /current_mode := 1 then
  286.     setup_tty()
  287.  
  288.     chr := reads(&input)
  289.     case chr of {
  290.     c_cc.vintr  : reset_tty() & stop()
  291.     c_cc.vquit  : reset_tty() & stop()
  292.     default : writes(chr) & return chr
  293.     }
  294.  
  295. end
  296.  
  297.  
  298.  
  299. procedure change_tty_mode(switch)
  300.  
  301.     # global c_cc   (global record containing values for kill, etc. chars)
  302.     local get_term_params, i
  303.     static reset_string
  304.     initial {
  305.     getval("li")    # check to be sure itlib is set up
  306.     find("unix",map(&features)) |
  307.         stop("change_tty_mode:  These routines must run under UNIX.")
  308.     get_term_params := open("/bin/stty -g 2>&1","pr")
  309.     reset_string := !get_term_params
  310.     close(get_term_params)
  311.     reset_string ? {
  312.         # tab upto the fifth field of the output of the stty -g cmd
  313.         # fields of stty -g seem to be the same as those of the
  314.         # termio struct, except that the c_line field is missing
  315.         every 1 to 4 do tab(find(":")+1)
  316.         c_cc := termio_struct("\x03","\x1C","\x08","\x15")
  317.         every i := 1 to 3 do {
  318.         c_cc[i] := char(integer("16r"||tab(find(":"))))
  319.         move(1)
  320.         }
  321.         c_cc[i+1] := char(integer("16r"||tab(0)))
  322.     }
  323.     }
  324.  
  325.     if switch == "setup"
  326.     then system("/bin/stty -echo raw")
  327.     else system("/bin/stty "||reset_string)
  328.  
  329.     return
  330.  
  331. end
  332.